package id

import (
	"database/sql"
	"fmt"
	"gitee.com/tziye/renai-go/common"
	"gitee.com/tziye/renai-go/renai"
	"gitee.com/tziye/renai-go/sqlproxy"
	"strconv"
	"sync"
	"time"
)

type IdOperator struct {
	RenaiClient *renai.RenaiClient
	Db          *sql.DB
}

func NewIdOperator(renaiClient *renai.RenaiClient) *IdOperator {
	return &IdOperator{
		RenaiClient: renaiClient,
		Db:          renaiClient.Db,
	}
}

const (
	cacheSize = 50
	stepTimes = 100
)

var cache = common.Cache(cacheSize)
var mutex = sync.Mutex{}

func (idOperator *IdOperator) Generate() int64 {
	// 18位通用Id
	idSpec := NewRenaiIdSpecWithNum(common.DEFAULT, EPOCHM, 5)
	return idOperator.GenerateId(idSpec)
}

func (idOperator *IdOperator) GenerateId(idSpec RenaiIdSpec) int64 {
	vr := idOperator.commonGenerate(idSpec)
	return idSpec.returnId(vr)
}

func (idOperator *IdOperator) GenerateCode(idSpec RenaiIdSpec) string {
	vr := idOperator.commonGenerate(idSpec)
	return idSpec.returnCode(vr)
}

func (idOperator *IdOperator) commonGenerate(idSpec RenaiIdSpec) common.ValueResult {
	mutex.Lock()
	defer mutex.Unlock()
	prefix := idSpec.Prefix
	timePattern := idSpec.time()
	step := idSpec.step()

	vr := common.ValueResult{}
	cacheKey := prefix + common.DELIMITER + timePattern
	cacheValue, ok := cache.Get(cacheKey)
	if ok {
		cacheStep, _ := cacheValue.(common.Tuple2)
		v1, _ := cacheStep.V1.(int64)
		v2, _ := cacheStep.V2.(int64)
		if v1+int64(step) <= v2 {
			vr.Lv = v1 + int64(step)
			vr.Sv = timePattern
			cache.Add(cacheKey, common.NewTuple2(v1+int64(step), v2))
			return vr
		}
	}
	bigStep := step * stepTimes
	vr = idOperator.doGenerate(prefix, timePattern, bigStep)
	realValue := vr.Lv - int64(bigStep) + int64(step)
	cache.Add(cacheKey, common.NewTuple2(realValue, vr.Lv))
	vr.Lv = realValue
	return vr
}

func (idOperator *IdOperator) doGenerate(prefix string, pattern string, step int) common.ValueResult {
	row := sqlproxy.CallRow(idOperator.Db, common.ID_GENERATE, prefix, pattern, step)
	var sv string
	var lv int64
	err := row.Scan(&sv, &lv)
	common.PanicErr(err)
	return common.ValueResult{Sv: sv, Lv: lv}
}

func (idOperator *IdOperator) Delete(prefix string) int {
	s := "DELETE FROM renai_id_generator WHERE prefix = ?"
	return sqlproxy.Exec(idOperator.Db, s, prefix)
}

func NewRenaiIdSpec(prefix string) RenaiIdSpec {
	return NewRenaiIdSpecWithPattern(prefix, YMD)
}

func NewRenaiIdSpecWithPattern(prefix string, pattern string) RenaiIdSpec {
	return NewRenaiIdSpecWithNum(prefix, pattern, 5)
}

func NewRenaiIdSpecWithNum(prefix string, pattern string, num int) RenaiIdSpec {
	return RenaiIdSpec{
		Prefix:  prefix,
		Pattern: pattern,
		Num:     num,
		Step:    1,
		Random:  false,
	}
}

type RenaiIdSpec struct {
	Prefix  string
	Pattern string
	Num     int
	Step    int
	Random  bool
}

func (idSpec RenaiIdSpec) time() string {
	return format(idSpec.Pattern)
}

func (idSpec RenaiIdSpec) step() int {
	if idSpec.Random {
		return 1 + common.Random(idSpec.Step)
	}
	return idSpec.Step
}

func (idSpec RenaiIdSpec) returnId(result common.ValueResult) int64 {
	seq := result.Lv
	length := len(strconv.FormatInt(seq, 10))
	if length < idSpec.Num {
		length = idSpec.Num
	}
	sequence := fmt.Sprintf("%0"+strconv.FormatInt(int64(length), 10)+"d", seq)
	id, err := strconv.ParseInt(result.Sv+sequence, 10, 64)
	common.PanicErr(err)
	return id
}

func (idSpec RenaiIdSpec) returnCode(result common.ValueResult) string {
	return idSpec.Prefix + strconv.FormatInt(idSpec.returnId(result), 10)
}

const (
	Y       = "06"
	YY      = "2006"
	YM      = "0601"
	YYM     = "200601"
	YMD     = "060102"
	YYMD    = "20060102"
	YMDH    = "06010215"
	YYMDH   = "2006010215"
	YMDHM   = "0601021504"
	YYMDHM  = "200601021504"
	YMDHMS  = "060102150405"
	YYMDHMS = "20060102150405"
	EPOCHS  = "EPOCH_S"
	EPOCHM  = "EPOCH_M"
)

func format(pattern string) string {
	if EPOCHS == pattern {
		return strconv.FormatInt(time.Now().Unix()/1000, 10)
	}
	if EPOCHM == pattern {
		return strconv.FormatInt(time.Now().Unix(), 10)
	}
	return common.TimeFormat(pattern, time.Now())
}
